package teg

import (
	"bufio"
	"bytes"
	"context"
	"encoding/binary"
	"encoding/json"
	"errors"
	"io"
	"log"
	"math"
	"net"
	"strings"
	"sync"
	"sync/atomic"
)

const (
	TEG_START   = 0
	TEG_GETLEN  = 1
	TEG_GETBODY = 2
	TEG_OVER    = 4
)

const (
	MAGIC_FLAG = "teg\n"
)

var ErrProto = errors.New("proto error")
var ErrLarge = errors.New("cross the line")
var ErrUnk = errors.New("unknown errors")

type LegProto struct {
	Closed atomic.Bool
}

func (h *LegProto) Action(ctx context.Context, conn net.Conn) {
	if h.Closed.Load() {
		conn.Close()
		return
	}

	wch := make(chan string)

	wctx, cannel := context.WithCancel(ctx)
	wg := sync.WaitGroup{}
	wg.Add(1)
	go reaction(wctx, conn, &wg, wch)
	r := bufio.NewReader(conn)
	for {
		body, err := GetBody(r)
		if err != nil {
			break
		}
		pp := PingPong{}
		if json.Unmarshal(body, &pp) != nil {
			log.Println(ErrPingPong)
			break
		}
		log.Println("get ping say :: ", pp.Say)
		pp.T = Pong
		ret, err := json.Marshal(pp)
		if err != nil {
			log.Println("json marshal error")
			continue
		}
		mes, err := MakeMessage(string(ret))
		if err != nil {
			break
		}
		wch <- string(mes)
	}
	cannel()
	wg.Wait()
	conn.Close()

	log.Println("game over")

}

func (h *LegProto) Close() error {
	h.Closed.Store(true)
	return nil
}

func reaction(ctx context.Context, conn net.Conn, wg *sync.WaitGroup, wch chan string) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			log.Println("done...!!")
			return
		case msg := <-wch:
			//todo
			var send = []byte(msg)
			var length = len(send)
			var start = 0

			for {
				n, err := conn.Write(send[start:])
				if err != nil {
					conn.Close()
					return
				}
				start += n
				if start >= length {
					break
				}

			}

		}
	}
}

func GetBody(r *bufio.Reader) ([]byte, error) {
	var state = TEG_START
	var bodyLen uint16
	for {
		switch state {
		case TEG_START:
			// log.Println("TEG_START")
			headString, err := r.ReadString('\n')
			if err != nil {
				return nil, err
			}
			if !strings.HasSuffix(headString, MAGIC_FLAG) {
				return nil, ErrProto
			}
			state = TEG_GETLEN
		case TEG_GETLEN:
			// log.Println("TEG_LEN")
			_, err := r.ReadByte()
			if err != nil {
				return nil, err
			}
			lenErr := binary.Read(r, binary.LittleEndian, &bodyLen)
			if lenErr != nil {
				return nil, lenErr
			}
			state = TEG_GETBODY
		case TEG_GETBODY:
			// log.Println("TEG_BODY")
			body := make([]byte, bodyLen)
			_, err := io.ReadFull(r, body)
			if err != nil {
				return nil, err
			}
			return body, nil
		default:
			return nil, ErrUnk
		}

	}
}

func MakeMessage(msg string) ([]byte, error) {
	var buf []byte
	bf := bytes.NewBuffer(buf)
	bf.WriteString(MAGIC_FLAG) //魔术字
	bf.WriteByte(0)            //版本

	var n = len(msg)
	if n > math.MaxUint16 {
		return nil, ErrLarge
	}
	binary.Write(bf, binary.LittleEndian, uint16(n))
	bf.WriteString(msg)
	return bf.Bytes(), nil

}
